R DATA VISUALIZATION

Raw data point doesn’t provide much insight to kick off data analysis.

Data Visualization is brilliant in - exploring the pattern of data briefly at the early stage - in the final conclusion, enhance the story telling of data analysis (inforgraphics play huge role for this purpose)

Built-in Plot Functions

The advantage of using built-in plotting utilities is they are easy. Let you quickly visually the data pattern while you are trying to gain some brief insight and prepare for your models.

Built-in Plot Tools

For built-in data visualization, go to the R Programming Intro project on Github to refresh your memory (R Intro Source Codes)[https://github.com/ngsanluk/R-Intro]

Grammar of Graphics: ggplot2

If these built-in plotting tools are not enough for you,  go fo ggplot2
the most popular data visualization for R.

ggplot2 is an open-source data visualization package for R. A data visualization which breaks up graphs into semantic components such as scales and layers. Since 2005, ggplot2 has grown in use to become one of the most popular R packages.

ggplot2 Cheat Sheet

ggplot 2 cheat sheet


BASIC GRAMMAR

ggplot2 is based on the grammar of graphics, the idea that you can build every graph from the same components: a data set + a coordinate system + and geoms—visual marks that represent data points

Grammar of Graphics

Use Built-in Datasets

Let’s use the built-in cars data set

print(cars)

geom_point() function

It’s easy to add geometry layer to the base co-ordinate
Let’s add a layer of data points.
Yes, you can add layer by using the + operator Let’s use point (namely geom_point()). In 2D co-ordinate, a point is describe by its x and y value.

We need to provide a mapping that specifies the data columns’ name to map to the x and y value of a point

That mapping is defined by an aesthetics function
aes()

Scatterplot is useful to explore the relation of two variable.

cars %>% ggplot() +
  geom_point(mapping = aes(x=speed, y=dist))

Use geom_line() to replace geom_point()

geom_point() and geom_line() require very similar parameters.
geom_line() is simply an enhanced visualization that automatically connect all the points

Use geom_smoth() to project a smooth line

again geom_smooth() and geom_point() require very similar parameters.
geom_smooth() smooths out the line progression

cars %>% ggplot() +
  geom_smooth(mapping = aes(x=speed, y=dist))
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

Adding Aesthetics to your Plots

cars %>% ggplot() +
  geom_point(mapping = aes(x=speed, y=dist),
             color = "orange", # the color of data points
             # size = 3, # the size of data point
             # alpha = 0.5, # the transparency of data points, min=0, max=1
             # shape = 0, # the shape of data point
             )


PLOT WITH OUR OWN DATA

Loading Data: allowance & graduates

allowance = read_csv("./data/allowance.csv")
Rows: 11 Columns: 13
── Column specification ─────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (2): Assessment_Year, Personal_Disability_Allowance
dbl (11): Basic, Married_Person, Child, Child_newborn, Dependent_Brother_Sister, Dependent_Parent_60, Dep...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
print(allowance)

Allowance Data Set in Simple Scatterplot

Continuous Values vs. Discrete Values

Continuous values refer to numbers value that has wide range Discrete values refer to a limited number of valid values. It can be string. It can be a few distinct numbers.

When you produce plots, pay attention to what type of value are required by the geoms.

In many case, you will need to convert the data first. mutate() function are quite often used for that.

example:

allowance = allowance %>% 
  mutate(Assessment_Year = as.numeric(substr(Assessment_Year, 1 ,4))) 

Simple Line Plot

Adding Multiple Layers of Geometry

Use geom_smooth() to smooth out the line

allowance %>% ggplot(aes(x=Assessment_Year, y=Basic, group=1, color="Orange")) +
  geom_smooth() +
  geom_point(size=5)
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

Save a Plot: ggsave()

my.first.plot = allowance %>% 
  ggplot() + 
  geom_point(
    mapping=aes(x=Assessment_Year, y=Basic),
    color = "orange",
    size = 3,
    ) 

print(my.first.plot)
  
ggsave("./output/my_first_plot.png") # default image size
Saving 7.29 x 4.51 in image
ggsave("./output/my_first_plot_large.png", width=10, height=10)

Bar Chart with geom_col()

allowance %>% 
  ggplot() +
  geom_col(mapping=aes(x=Assessment_Year, y=Basic),
           fill="tomato") 

Histogram with geom_bar()

Counting the frequency of each occurrence of observed value.


CHALLENGE

Multiple Layers of Lines

add line plot for coloumn of Child in the same plot, add another line plot for Dependent_Parent_60

allowance %>% ggplot() +
  geom_line(mapping = aes(x=Assessment_Year, y=Child, group=1), color="Orange") + 
  geom_line(mapping = aes(x=Assessment_Year, y=Dependent_Parent_60, group=1), color="Blue") 


WORK WITH MORE COMPLEX DATA

Loading Data: graduates.csv

graduates = read_csv("./data/graduates.csv")
Rows: 601 Columns: 5
── Column specification ─────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (4): AcademicYear, LevelOfStudy, ProgrammeCategory, Sex
dbl (1): Headcount

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
print(graduates)

Simple Scatterplot

Let’s explore the data with some simple ggplot plot. Overall they are not very useful. Just some quick exploration.

ggplot(data=graduates) +
  geom_point(mapping=aes(x=AcademicYear, y=Headcount))


ggplot(data=graduates) +
  geom_point(mapping=aes(x=AcademicYear, y=Headcount, shape=Sex)
             )


ggplot(data=graduates) +
  geom_point(mapping=aes(x=AcademicYear, y=Headcount, color=Sex)
             )

Use filter() to Extract Required Rows

graduates %>% 
filter(LevelOfStudy=="Undergraduate", ProgrammeCategory=="Business and Management") %>%
ggplot() +
  geom_point(mapping=aes(x=AcademicYear, y=Headcount, color=Sex)
)

CHALLENGE

Comparison with Line Plots

Use line plots to compare female undergraduate students headcount trending in ProgrammeCategory of “Business and Management” and “Engineering and Technology” Use filter() to extract required record You can use multiple filter() call Use &, | or multiple conditions


graduates %>% 
  .$ProgrammeCategory %>% 
  unique() # display the unique names of ProgrammeCategory

graduates %>% 
  filter(LevelOfStudy=="Undergraduate", Sex=="F") %>%
  filter(ProgrammeCategory=="Business and Management" | ProgrammeCategory=="Engineering and Technology") %>% 
  print() # Test extracting and printing the required records.

graduates %>% 
  filter(LevelOfStudy=="Undergraduate", Sex=="F") %>%
  filter(ProgrammeCategory=="Business and Management" | ProgrammeCategory=="Engineering and Technology") %>% 
  ggplot(
    aes(x=AcademicYear, 
             y=Headcount,
             group=ProgrammeCategory, 
             color=ProgrammeCategory
             )
    ) +
    geom_line() +
    geom_point() 
  

CHALLENGE: line plot for hibor_fixing_1m


GROUPING AND AGGREGATION

Using group_by() and summarise()

graduates %>% group_by(AcademicYear, LevelOfStudy) %>% 
  summarise(TotalHeadcount = sum(Headcount)) 
`summarise()` has grouped output by 'AcademicYear'. You can override using the `.groups` argument.
graduates %>% group_by(AcademicYear, LevelOfStudy) %>% 
  summarise(TotalHeadcount = sum(Headcount)) %>% 
  ggplot(
    aes(x=AcademicYear, 
             y=TotalHeadcount,
             group=LevelOfStudy, 
             color=LevelOfStudy
             )
    ) +
    geom_line() +
    geom_point() 
`summarise()` has grouped output by 'AcademicYear'. You can override using the `.groups` argument.

NA

Use of filter()

Use filter() to keep only “Taught Postgraduate” Records

This plot is not very useful without previously applying filter() and group_by() and summarise()

filter() + group_by() + summarise()

Use filter() to extract required rows Use group_by() and summarise() to group and aggreate total headcout for both male and female

graduates %>% 
  filter(LevelOfStudy=="Undergraduate") %>% 
  group_by(AcademicYear, ProgrammeCategory) %>% 
  summarise(TotalHeadcount = sum(Headcount)) %>% 
  ggplot() +
    geom_line(mapping=aes(x=AcademicYear,y=TotalHeadcount, group=ProgrammeCategory, color=ProgrammeCategory))
`summarise()` has grouped output by 'AcademicYear'. You can override using the `.groups` argument.

geom_col() function

More Aggregation Functions

Center: mean(), median() Spread: sd(), IQR(), mad() Range: min(), max(), quantile() Position: first(), last(), nth(), Count: n(), n_distinct() Logical: any(), all()

More information at summarise() function

geom_bar() function

bar chart give the counting frequency (number of record in the data set)

box plot

The boxplot compactly displays the distribution of a continuous variable. It visualises five summary statistics (the median, two hinges and two whiskers), and all “outlying” points individually.

graduates %>% 
ggplot() +
  geom_point(mapping=aes(x=Sex, y=Headcount))

graduates %>% 
ggplot() +
  geom_boxplot(mapping=aes(x=LevelOfStudy, y=Headcount))

MAKE IT PRETTY

Use of title, label, background color and themes

Plot Background

level.bar.plot # default style


level.bar.plot +
  theme(plot.background = element_rect(fill="orange"))

Panel Background

level.bar.plot # default style


level.bar.plot +
  theme(panel.background = element_rect(fill="orange")) # styling the panel background

Remove Plot and Panel Background

level.bar.plot # default style


level.bar.plot +
  theme(panel.background = element_blank()) + 
  theme(plot.background = element_blank()) +
  theme(panel.grid.major.y = element_line(color="grey"))

Label

level.bar.plot # default style


level.bar.plot +
  theme(panel.background = element_blank()) + # styling the panel background to none
  theme(plot.background = element_blank()) + # styling the plot background to none
  theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
  ylab("Number of Student") + # Label for Y axis
  xlab("Year") # Label for X axis

Change Fill Colors

level.bar.plot # default style


level.bar.plot +
  theme(panel.background = element_blank()) + # styling the panel background to none
  theme(plot.background = element_blank()) + # styling the plot background to none
  theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
  ylab("Number of Student") + # Label for Y axis
  xlab("Year") + # Label for X axis
  scale_fill_manual(values=c("purple", "orange", "blue", "tomato"))

Styling Legends

level.bar.plot # default style


level.bar.plot +
  theme(panel.background = element_blank()) + # styling the panel background to none
  theme(plot.background = element_blank()) + # styling the plot background to none
  theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
  ylab("Number of Student") + # Label for Y axis
  xlab("Year") + # Label for X axis
  theme(legend.position="top") +
  scale_fill_manual(values=c("purple", "orange", "blue", "tomato"),
                    guide = guide_legend(title="Level of Study", 
                                         label.position = "bottom")
                    )

NA

Title

level.bar.plot # default style


level.bar.plot +
  theme(panel.background = element_blank()) + # styling the panel background to none
  theme(plot.background = element_blank()) + # styling the plot background to none
  theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
  ylab("Number of Student") + # Label for Y axis
  xlab("Year") + # Label for X axis
  theme(legend.position="top") +
  scale_fill_manual(values=c("purple", "orange", "blue", "tomato"),
                    guide = guide_legend(title="Level of Study", 
                                         label.position = "bottom")
                    ) + # move legend position to top and label position to bottom 
  ggtitle("Hong Kong Higher Education Student Headcount", subtitle="2009 - 2019")

Adding Annotations Text

Add extra texts/shape to enhance your visualization

level.bar.plot # default style


level.bar.plot +
  theme(panel.background = element_blank()) + # styling the panel background to none
  theme(plot.background = element_blank()) + # styling the plot background to none
  theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
  ylab("Number of Student") + # Label for Y axis
  xlab("Year") + # Label for X axis
  theme(legend.position="top") +
  scale_fill_manual(values=c("purple", "orange", "blue", "tomato"),
                    guide = guide_legend(title="Level of Study", 
                                         label.position = "bottom")
                    ) + # move legend position to top and label position to bottom 
  ggtitle("Hong Kong Higher Education Student Headcount", subtitle="2009 - 2019") + 
  annotate("text", label="Record\nHigh", x="2017/18", y=5300) # you can change value of x and y to set the text position

Adding Reference Line

level.bar.plot # default style


level.bar.plot +
  theme(panel.background = element_blank()) + # styling the panel background to none
  theme(plot.background = element_blank()) + # styling the plot background to none
  theme(panel.grid.major.y = element_line(color="grey")) + # styling the grid line for y-axis
  ylab("Number of Student") + # Label for Y axis
  xlab("Year") + # Label for X axis
  theme(legend.position="top") +
  scale_fill_manual(values=c("purple", "orange", "blue", "tomato"),
                    guide = guide_legend(title="Level of Study", 
                                         label.position = "bottom")
                    ) + # move legend position to top and label position to bottom 
  ggtitle("Hong Kong Higher Education Student Headcount", subtitle="2009 - 2019") + 
  annotate("text", label="Record\nHigh", x="2017/18", y=5300) + # you can change text position value of x and y to set the text position
  geom_hline(yintercept=3200) + # adds horizontal line
  geom_vline(xintercept = "2017/18") # adds vertical line

NA

Using Themes

level.bar.plot # default style


level.bar.plot +
  theme_bw() # black and white theme


level.bar.plot +
  theme_minimal() # black and white theme


level.bar.plot +
  theme_dark() # black and white theme

More 3rd-party Themes

Install ggthemes package to unlock wider selections of themes.

if (!require("pacman")) install.packages("pacman") # check if pacman already installed. If not, install it.
pacman::p_load(ggthemes)

level.bar.plot # default style


level.bar.plot +
  theme_excel() # Excel Theme


level.bar.plot +
  theme_wsj() # Wall Street Journal Theme


level.bar.plot +
  theme_economist() # Economist Theme


level.bar.plot +
  theme_fivethirtyeight() # Wall Street Journal Theme


MORE RESOURCES ON ggplot2

official website

browseURL("https://ggplot2.tidyverse.org/")

extentsions

browseURL("https://exts.ggplot2.tidyverse.org/")

MODELING


TO CONTINUE

unnest()

Categorical Variable

Recoding Data

Scaling

Transforming Outliers

LS0tCnRpdGxlOiAiUiBJbnRlcm1lZGlhdGUgLSBEYXkgMiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoKIyBSIERBVEEgVklTVUFMSVpBVElPTgoKUmF3IGRhdGEgcG9pbnQgZG9lc24ndCBwcm92aWRlIG11Y2ggaW5zaWdodCB0byBraWNrIG9mZiBkYXRhIGFuYWx5c2lzLlwKCkRhdGEgVmlzdWFsaXphdGlvbiBpcyBicmlsbGlhbnQgaW4KLSBleHBsb3JpbmcgdGhlIHBhdHRlcm4gb2YgZGF0YSBicmllZmx5IGF0IHRoZSBlYXJseSBzdGFnZQotIGluIHRoZSBmaW5hbCBjb25jbHVzaW9uLCBlbmhhbmNlIHRoZSBzdG9yeSB0ZWxsaW5nIG9mIGRhdGEgYW5hbHlzaXMgKGluZm9yZ3JhcGhpY3MgcGxheSBodWdlIHJvbGUgZm9yIHRoaXMgcHVycG9zZSkKCiMjIEJ1aWx0LWluIFBsb3QgRnVuY3Rpb25zClRoZSBhZHZhbnRhZ2Ugb2YgdXNpbmcgYnVpbHQtaW4gcGxvdHRpbmcgdXRpbGl0aWVzIGlzIHRoZXkgYXJlIGVhc3kuCkxldCB5b3UgcXVpY2tseSB2aXN1YWxseSB0aGUgZGF0YSBwYXR0ZXJuIHdoaWxlIHlvdSBhcmUgdHJ5aW5nIHRvIGdhaW4gc29tZSBicmllZiBpbnNpZ2h0IGFuZCBwcmVwYXJlIGZvciB5b3VyIG1vZGVscy4KYGBge3J9CnBsb3QoaXJpcykKYGBgCgojIyBCdWlsdC1pbiBQbG90IFRvb2xzCkZvciBidWlsdC1pbiBkYXRhIHZpc3VhbGl6YXRpb24sIGdvIHRvIHRoZSBSIFByb2dyYW1taW5nIEludHJvIHByb2plY3Qgb24gR2l0aHViIHRvIHJlZnJlc2ggeW91ciBtZW1vcnkKKFIgSW50cm8gU291cmNlIENvZGVzKVtodHRwczovL2dpdGh1Yi5jb20vbmdzYW5sdWsvUi1JbnRyb10KCgojIyBHcmFtbWFyIG9mIEdyYXBoaWNzOiBnZ3Bsb3QyCgpJZiB0aGVzZSBidWlsdC1pbiBwbG90dGluZyB0b29scyBhcmUgbm90IGVub3VnaCBmb3IgeW91LFwgCmdvIGZvICoqZ2dwbG90MioqXAp0aGUgbW9zdCBwb3B1bGFyIGRhdGEgdmlzdWFsaXphdGlvbiBmb3IgUi4KCmdncGxvdDIgaXMgYW4gb3Blbi1zb3VyY2UgZGF0YSB2aXN1YWxpemF0aW9uIHBhY2thZ2UgZm9yIFIuIEEgZGF0YSB2aXN1YWxpemF0aW9uIHdoaWNoIGJyZWFrcyB1cCBncmFwaHMgaW50byBzZW1hbnRpYyBjb21wb25lbnRzIHN1Y2ggYXMgc2NhbGVzIGFuZCBsYXllcnMuIFNpbmNlIDIwMDUsIGdncGxvdDIgaGFzIGdyb3duIGluIHVzZSB0byBiZWNvbWUgb25lIG9mIHRoZSBtb3N0IHBvcHVsYXIgUiBwYWNrYWdlcy4KCgojIyBnZ3Bsb3QyIENoZWF0IFNoZWV0CgpbZ2dwbG90IDIgY2hlYXQgc2hlZXRdKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL2Jsb2IvbWFpbi9kYXRhLXZpc3VhbGl6YXRpb24tMi4xLnBkZikKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBCQVNJQyBHUkFNTUFSCmdncGxvdDIgaXMgYmFzZWQgb24gdGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MsIHRoZSBpZGVhIHRoYXQgeW91IGNhbiBidWlsZCBldmVyeSBncmFwaCBmcm9tIHRoZSBzYW1lIGNvbXBvbmVudHM6ICoqYSBkYXRhIHNldCoqIAorIAoqKmEgY29vcmRpbmF0ZSBzeXN0ZW0qKiAKKyAKKiphbmQgZ2VvbXPigJR2aXN1YWwgbWFya3MgdGhhdCByZXByZXNlbnQgZGF0YSBwb2ludHMqKgoKIVtHcmFtbWFyIG9mIEdyYXBoaWNzXShodHRwczovL2p1bGVzMzIuZ2l0aHViLmlvL3ItZm9yLWV4Y2VsLXVzZXJzL2ltZy9yc3R1ZGlvLWNoZWF0c2hlZXQtZ2dwbG90LnBuZykKCgojIyBVc2UgQnVpbHQtaW4gRGF0YXNldHMKTGV0J3MgdXNlIHRoZSBidWlsdC1pbiBjYXJzIGRhdGEgc2V0CgpgYGB7cn0KcHJpbnQoY2FycykKYGBgCgpgYGB7ciBHZW5lcmF0aW5nIEVtcHR5IFBsb3R9CmNhcnMgJT4lIGdncGxvdCgpICMgVGhpcyBvbmx5IHNwZWNpZmllcyBhIGRhdGEgc2V0IGFuZCBhIGNvb3JkaW5hdGUgc3lzdGVtIGFuZCB0aGVyZWZvcmUgYW4gZW1wdHkgcGxvdApgYGAKCiMjIGdlb21fcG9pbnQoKSBmdW5jdGlvbgpJdCdzIGVhc3kgdG8gYWRkIGdlb21ldHJ5IGxheWVyIHRvIHRoZSBiYXNlIGNvLW9yZGluYXRlXApMZXQncyBhZGQgYSBsYXllciBvZiBkYXRhIHBvaW50cy4gXApZZXMsIHlvdSBjYW4gYWRkIGxheWVyIGJ5IHVzaW5nIHRoZSArIG9wZXJhdG9yCkxldCdzIHVzZSBwb2ludCAobmFtZWx5ICoqZ2VvbV9wb2ludCgpKiopLgpJbiAyRCBjby1vcmRpbmF0ZSwgYSBwb2ludCBpcyBkZXNjcmliZSBieSBpdHMgeCBhbmQgeSB2YWx1ZS4KCldlIG5lZWQgdG8gcHJvdmlkZSBhIG1hcHBpbmcgdGhhdCBzcGVjaWZpZXMgdGhlIGRhdGEgY29sdW1ucycgbmFtZSB0byBtYXAgdG8gdGhlIHggYW5kIHkgdmFsdWUgb2YgYSBwb2ludAoKVGhhdCBtYXBwaW5nIGlzIGRlZmluZWQgYnkgYW4gYWVzdGhldGljcyBmdW5jdGlvblwKKiphZXMoKSoqCgpTY2F0dGVycGxvdCBpcyB1c2VmdWwgdG8gZXhwbG9yZSB0aGUgcmVsYXRpb24gb2YgdHdvIHZhcmlhYmxlLgoKYGBge3J9CmNhcnMgJT4lIGdncGxvdCgpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeD1zcGVlZCwgeT1kaXN0KSkKYGBgCgojIyBVc2UgZ2VvbV9saW5lKCkgdG8gcmVwbGFjZSBnZW9tX3BvaW50KCkKZ2VvbV9wb2ludCgpIGFuZCBnZW9tX2xpbmUoKSByZXF1aXJlIHZlcnkgc2ltaWxhciBwYXJhbWV0ZXJzLlwKZ2VvbV9saW5lKCkgaXMgc2ltcGx5IGFuIGVuaGFuY2VkIHZpc3VhbGl6YXRpb24gdGhhdCBhdXRvbWF0aWNhbGx5IGNvbm5lY3QgYWxsIHRoZSBwb2ludHMKCmBgYHtyfQpjYXJzICU+JSBnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKG1hcHBpbmcgPSBhZXMoeD1zcGVlZCwgeT1kaXN0KSkgIyBqdXN0IGNoYW5nZSBnZW9tX3BvaW50IHRvIGdlb21fbGluZSB3aXRob3V0IGNoYW5nZSBhbnl0aGluZyBlbHNlCmBgYAoKCiMjIFVzZSBnZW9tX3Ntb3RoKCkgdG8gcHJvamVjdCBhIHNtb290aCBsaW5lCmFnYWluIGdlb21fc21vb3RoKCkgYW5kIGdlb21fcG9pbnQoKSByZXF1aXJlIHZlcnkgc2ltaWxhciBwYXJhbWV0ZXJzLlwKZ2VvbV9zbW9vdGgoKSBzbW9vdGhzIG91dCB0aGUgbGluZSBwcm9ncmVzc2lvbgoKYGBge3J9CmNhcnMgJT4lIGdncGxvdCgpICsKICBnZW9tX3Ntb290aChtYXBwaW5nID0gYWVzKHg9c3BlZWQsIHk9ZGlzdCkpICMganVzdCBjaGFuZ2UgZ2VvbV9wb2ludCB0byBnZW9tX2xpbmUgd2l0aG91dCBjaGFuZ2UgYW55dGhpbmcgZWxzZQpgYGAKCiMjIEFkZGluZyBBZXN0aGV0aWNzIHRvIHlvdXIgUGxvdHMKYGBge3J9CmNhcnMgJT4lIGdncGxvdCgpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeD1zcGVlZCwgeT1kaXN0KSwKICAgICAgICAgICAgIGNvbG9yID0gIm9yYW5nZSIsICMgdGhlIGNvbG9yIG9mIGRhdGEgcG9pbnRzCiAgICAgICAgICAgICAjIHNpemUgPSAzLCAjIHRoZSBzaXplIG9mIGRhdGEgcG9pbnQKICAgICAgICAgICAgICMgYWxwaGEgPSAwLjUsICMgdGhlIHRyYW5zcGFyZW5jeSBvZiBkYXRhIHBvaW50cywgbWluPTAsIG1heD0xCiAgICAgICAgICAgICAjIHNoYXBlID0gMCwgIyB0aGUgc2hhcGUgb2YgZGF0YSBwb2ludAogICAgICAgICAgICAgKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBQTE9UIFdJVEggT1VSIE9XTiBEQVRBCgojIyBMb2FkaW5nIERhdGE6IGFsbG93YW5jZSAmIGdyYWR1YXRlcwpgYGB7ciByZWFkaW5nIGRhdGEgZmlsZXN9CmFsbG93YW5jZSA9IHJlYWRfY3N2KCIuL2RhdGEvYWxsb3dhbmNlLmNzdiIpCnByaW50KGFsbG93YW5jZSkKYGBgCgoKIyMgQWxsb3dhbmNlIERhdGEgU2V0IGluIFNpbXBsZSBTY2F0dGVycGxvdApgYGB7ciBBbGxvd2FuY2UgU2NhdHRlcnBsb3R9CmFsbG93YW5jZSAlPiUgCiAgZ2dwbG90KCkgKyAKICBnZW9tX3BvaW50KAogICAgbWFwcGluZz1hZXMoeD1Bc3Nlc3NtZW50X1llYXIsIHk9QmFzaWMpLAogICAgY29sb3IgPSAib3JhbmdlIiwKICAgIHNpemUgPSAzCiAgICApIApgYGAKCgoKCiMjIENvbnRpbnVvdXMgVmFsdWVzIHZzLiBEaXNjcmV0ZSBWYWx1ZXMKQ29udGludW91cyB2YWx1ZXMgcmVmZXIgdG8gbnVtYmVycyB2YWx1ZSB0aGF0IGhhcyB3aWRlIHJhbmdlCkRpc2NyZXRlIHZhbHVlcyByZWZlciB0byBhIGxpbWl0ZWQgbnVtYmVyIG9mIHZhbGlkIHZhbHVlcy4gIEl0IGNhbiBiZSBzdHJpbmcuIEl0IGNhbiBiZSBhIGZldyBkaXN0aW5jdCBudW1iZXJzLgoKV2hlbiB5b3UgcHJvZHVjZSBwbG90cywgcGF5IGF0dGVudGlvbiB0byB3aGF0IHR5cGUgb2YgdmFsdWUgYXJlIHJlcXVpcmVkIGJ5IHRoZSBnZW9tcy4KCkluIG1hbnkgY2FzZSwgeW91IHdpbGwgbmVlZCB0byBjb252ZXJ0IHRoZSBkYXRhIGZpcnN0LgoqKm11dGF0ZSgpKiogZnVuY3Rpb24gYXJlIHF1aXRlIG9mdGVuIHVzZWQgZm9yIHRoYXQuCgpleGFtcGxlOgpgYGB7cn0KYWxsb3dhbmNlID0gYWxsb3dhbmNlICU+JSAKICBtdXRhdGUoQXNzZXNzbWVudF9ZZWFyID0gYXMubnVtZXJpYyhzdWJzdHIoQXNzZXNzbWVudF9ZZWFyLCAxICw0KSkpIApgYGAKCgojIyBTaW1wbGUgTGluZSBQbG90CmBgYHtyIEFsbG93YW5jZSBMaW5lIEdyYXBofQoKIyBUaGUgZm9sbG93aW5nIHN0YXRlbWVudCB3b24ndCBnZW5lcmF0ZSBhIHBsb3QKYWxsb3dhbmNlICU+JSBnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKG1hcHBpbmcgPSBhZXMoeD1Bc3Nlc3NtZW50X1llYXIsIHk9QmFzaWMpKQoKIyBGb3IgbGluZSBncmFwaHMsIHRoZSBkYXRhIHBvaW50cyBtdXN0IGJlIGdyb3VwZWQgc28gdGhhdCBpdCBrbm93cyB3aGljaCBwb2ludHMgdG8gY29ubmVjdC4gCiMgSW4gdGhpcyBjYXNlLCBhbGwgcG9pbnRzIHNob3VsZCBiZSBjb25uZWN0ZWQsIHNvIGdyb3VwPTEuIAojIFdoZW4gbW9yZSB2YXJpYWJsZXMgYXJlIHVzZWQgYW5kIG11bHRpcGxlIGxpbmVzIGFyZSBkcmF3biwgdGhlIGdyb3VwaW5nIGZvciBsaW5lcyBpcyB1c3VhbGx5IGRvbmUgYnkgdmFyaWFibGUuCmFsbG93YW5jZSAlPiUgZ2dwbG90KCkgKwogIGdlb21fbGluZShtYXBwaW5nID0gYWVzKHg9QXNzZXNzbWVudF9ZZWFyLCB5PUJhc2ljLCBncm91cD0xLCBjb2xvcj0iT3JhbmdlIikpCmBgYAoKIyMgQWRkaW5nIE11bHRpcGxlIExheWVycyBvZiBHZW9tZXRyeQpgYGB7cn0KYWxsb3dhbmNlICU+JSBnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKG1hcHBpbmcgPSBhZXMoeD1Bc3Nlc3NtZW50X1llYXIsIHk9QmFzaWMsIGdyb3VwPTEsIGNvbG9yPSJPcmFuZ2UiKSkgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4PUFzc2Vzc21lbnRfWWVhciwgeT1CYXNpYywgZ3JvdXA9MSwgY29sb3I9Ik9yYW5nZSIpKQoKIyBBcyBib3RoIGdlb20gdXNlIHRoZSBzYW1lIGRhdGEgbWFwcGluZywgdGhlIGFib3ZlIHN0YXRlbWVudHMgY2FuIGJlIHNpbXBsaWZpZWQgYXMKYWxsb3dhbmNlICU+JSBnZ3Bsb3QoYWVzKHg9QXNzZXNzbWVudF9ZZWFyLCB5PUJhc2ljLCBncm91cD0xLCBjb2xvcj0iT3JhbmdlIikpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludChzaXplPTUpCmBgYAoKIyMgVXNlIGdlb21fc21vb3RoKCkgdG8gc21vb3RoIG91dCB0aGUgbGluZQpgYGB7cn0KYWxsb3dhbmNlICU+JSBnZ3Bsb3QoYWVzKHg9QXNzZXNzbWVudF9ZZWFyLCB5PUJhc2ljLCBncm91cD0xLCBjb2xvcj0iT3JhbmdlIikpICsKICBnZW9tX3Ntb290aCgpICsKICBnZW9tX3BvaW50KHNpemU9NSkKYGBgCgoKCiMjIFNhdmUgYSBQbG90OiBnZ3NhdmUoKQpgYGB7cn0KbXkuZmlyc3QucGxvdCA9IGFsbG93YW5jZSAlPiUgCiAgZ2dwbG90KCkgKyAKICBnZW9tX3BvaW50KAogICAgbWFwcGluZz1hZXMoeD1Bc3Nlc3NtZW50X1llYXIsIHk9QmFzaWMpLAogICAgY29sb3IgPSAib3JhbmdlIiwKICAgIHNpemUgPSAzLAogICAgKSAKCnByaW50KG15LmZpcnN0LnBsb3QpCgpnZ3NhdmUoIi4vb3V0cHV0L215X2ZpcnN0X3Bsb3QucG5nIikgIyBkZWZhdWx0IGltYWdlIHNpemUKZ2dzYXZlKCIuL291dHB1dC9teV9maXJzdF9wbG90X2xhcmdlLnBuZyIsIHdpZHRoPTEwLCBoZWlnaHQ9MTApCgpgYGAKCiMjIEJhciBDaGFydCB3aXRoIGdlb21fY29sKCkKCmBgYHtyfQphbGxvd2FuY2UgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2NvbChtYXBwaW5nPWFlcyh4PUFzc2Vzc21lbnRfWWVhciwgeT1CYXNpYyksCiAgICAgICAgICAgZmlsbD0idG9tYXRvIikgCmBgYAoKCgoKIyMgSGlzdG9ncmFtIHdpdGggZ2VvbV9iYXIoKQpDb3VudGluZyB0aGUgZnJlcXVlbmN5IG9mIGVhY2ggb2NjdXJyZW5jZSBvZiBvYnNlcnZlZCB2YWx1ZS4KCmBgYHtyfQphbGxvd2FuY2UgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2JhcihtYXBwaW5nPWFlcyh4PUJhc2ljKSkgIApgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBDSEFMTEVOR0UKIyMgTXVsdGlwbGUgTGF5ZXJzIG9mIExpbmVzCmFkZCBsaW5lIHBsb3QgZm9yIGNvbG91bW4gb2YgQ2hpbGQKaW4gdGhlIHNhbWUgcGxvdCwgYWRkIGFub3RoZXIgbGluZSBwbG90IGZvciBEZXBlbmRlbnRfUGFyZW50XzYwCmBgYHtyfQphbGxvd2FuY2UgJT4lIGdncGxvdCgpICsKICBnZW9tX2xpbmUobWFwcGluZyA9IGFlcyh4PUFzc2Vzc21lbnRfWWVhciwgeT1DaGlsZCwgZ3JvdXA9MSksIGNvbG9yPSJPcmFuZ2UiKSArIAogIGdlb21fbGluZShtYXBwaW5nID0gYWVzKHg9QXNzZXNzbWVudF9ZZWFyLCB5PURlcGVuZGVudF9QYXJlbnRfNjAsIGdyb3VwPTEpLCBjb2xvcj0iQmx1ZSIpIApgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBXT1JLIFdJVEggTU9SRSBDT01QTEVYIERBVEEKCiMjIExvYWRpbmcgRGF0YTogZ3JhZHVhdGVzLmNzdgpgYGB7ciByZWFkaW5nIGNvbXBsZXggZGF0YX0KZ3JhZHVhdGVzID0gcmVhZF9jc3YoIi4vZGF0YS9ncmFkdWF0ZXMuY3N2IikKcHJpbnQoZ3JhZHVhdGVzKQpgYGAKCiMjIFNpbXBsZSBTY2F0dGVycGxvdApMZXQncyBleHBsb3JlIHRoZSBkYXRhIHdpdGggc29tZSBzaW1wbGUgZ2dwbG90IHBsb3QuIApPdmVyYWxsIHRoZXkgYXJlIG5vdCB2ZXJ5IHVzZWZ1bC4gIEp1c3Qgc29tZSBxdWljayBleHBsb3JhdGlvbi4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1ncmFkdWF0ZXMpICsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9QWNhZGVtaWNZZWFyLCB5PUhlYWRjb3VudCkpCgpnZ3Bsb3QoZGF0YT1ncmFkdWF0ZXMpICsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9QWNhZGVtaWNZZWFyLCB5PUhlYWRjb3VudCwgc2hhcGU9U2V4KSAjIHVzZSBzaGFwZSB0byBkaWZmZXJlbnRpYXRlIGdyb3VwcwogICAgICAgICAgICAgKQoKZ2dwbG90KGRhdGE9Z3JhZHVhdGVzKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PUFjYWRlbWljWWVhciwgeT1IZWFkY291bnQsIGNvbG9yPVNleCkgIyB1c2UgY29sb3IgdG8gZGlmZmVyZW50aWF0ZSBncm91cHMKICAgICAgICAgICAgICkKYGBgCgojIyBVc2UgZmlsdGVyKCkgdG8gRXh0cmFjdCBSZXF1aXJlZCBSb3dzCmBgYHtyIHVzZSBmaWx0ZXIoKX0KZ3JhZHVhdGVzICU+JSAKZmlsdGVyKExldmVsT2ZTdHVkeT09IlVuZGVyZ3JhZHVhdGUiLCBQcm9ncmFtbWVDYXRlZ29yeT09IkJ1c2luZXNzIGFuZCBNYW5hZ2VtZW50IikgJT4lCmdncGxvdCgpICsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9QWNhZGVtaWNZZWFyLCB5PUhlYWRjb3VudCwgY29sb3I9U2V4KQopCmBgYAoKCiMjIFVzZSBMaW5lIFBsb3QgVG8gRXhwbG9yZSB0aGUgVHJlbmRpbmcgClVzZSBsaW5lIHBsb3QgdG8gZXhwbG9yZSB0aGUgdHJlbmRpbmcgb2YgIkJ1c2luZXNzIGFuZCBNYW5hZ2VtZW50IiBzdHVkZW50IGhlYWRjb3VudCB0cmVuZGluZwoKCmBgYHtyfQpsaWJyYXJ5KG1hZ3JpdHRyKQpncmFkdWF0ZXMgJTw+JQogIG11dGF0ZShBY2FkZW1pY1llYXI9YXMuZmFjdG9yKEFjYWRlbWljWWVhciksCiAgICAgICAgIFNleD1hcy5mYWN0b3IoU2V4KQogICAgICAgICApICMgY29udmVydCB0aGUgQWNhZGVtaWNZZWFyIGFuZCBTZXggdG8gZmFjdG9yIHR5cGUKCmdyYWR1YXRlcyAlPiUgCmZpbHRlcihMZXZlbE9mU3R1ZHk9PSJVbmRlcmdyYWR1YXRlIiwgUHJvZ3JhbW1lQ2F0ZWdvcnk9PSJCdXNpbmVzcyBhbmQgTWFuYWdlbWVudCIpICU+JQpnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKAogICAgbWFwcGluZz1hZXMoeD1BY2FkZW1pY1llYXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgeT1IZWFkY291bnQsCiAgICAgICAgICAgICAgICAgICAgICAgICBncm91cD1TZXgsIAogICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9U2V4CiAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICApCgpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBDSEFMTEVOR0UKIyMgQ29tcGFyaXNvbiB3aXRoIExpbmUgUGxvdHMKVXNlIGxpbmUgcGxvdHMgdG8gY29tcGFyZSBmZW1hbGUgdW5kZXJncmFkdWF0ZSBzdHVkZW50cyBoZWFkY291bnQgdHJlbmRpbmcgaW4gUHJvZ3JhbW1lQ2F0ZWdvcnkgb2YgIkJ1c2luZXNzIGFuZCBNYW5hZ2VtZW50IiBhbmQgIkVuZ2luZWVyaW5nIGFuZCBUZWNobm9sb2d5IgpVc2UgZmlsdGVyKCkgdG8gZXh0cmFjdCByZXF1aXJlZCByZWNvcmQKWW91IGNhbiB1c2UgbXVsdGlwbGUgZmlsdGVyKCkgY2FsbApVc2UgJiwgfCBvciBtdWx0aXBsZSBjb25kaXRpb25zCgpgYGB7cn0KCmdyYWR1YXRlcyAlPiUgCiAgLiRQcm9ncmFtbWVDYXRlZ29yeSAlPiUgCiAgdW5pcXVlKCkgIyBkaXNwbGF5IHRoZSB1bmlxdWUgbmFtZXMgb2YgUHJvZ3JhbW1lQ2F0ZWdvcnkKCmdyYWR1YXRlcyAlPiUgCiAgZmlsdGVyKExldmVsT2ZTdHVkeT09IlVuZGVyZ3JhZHVhdGUiLCBTZXg9PSJGIikgJT4lCiAgZmlsdGVyKFByb2dyYW1tZUNhdGVnb3J5PT0iQnVzaW5lc3MgYW5kIE1hbmFnZW1lbnQiIHwgUHJvZ3JhbW1lQ2F0ZWdvcnk9PSJFbmdpbmVlcmluZyBhbmQgVGVjaG5vbG9neSIpICU+JSAKICBwcmludCgpICMgVGVzdCBleHRyYWN0aW5nIGFuZCBwcmludGluZyB0aGUgcmVxdWlyZWQgcmVjb3Jkcy4KCmdyYWR1YXRlcyAlPiUgCiAgZmlsdGVyKExldmVsT2ZTdHVkeT09IlVuZGVyZ3JhZHVhdGUiLCBTZXg9PSJGIikgJT4lCiAgZmlsdGVyKFByb2dyYW1tZUNhdGVnb3J5PT0iQnVzaW5lc3MgYW5kIE1hbmFnZW1lbnQiIHwgUHJvZ3JhbW1lQ2F0ZWdvcnk9PSJFbmdpbmVlcmluZyBhbmQgVGVjaG5vbG9neSIpICU+JSAKICBnZ3Bsb3QoCiAgICBhZXMoeD1BY2FkZW1pY1llYXIsIAogICAgICAgICAgICAgeT1IZWFkY291bnQsCiAgICAgICAgICAgICBncm91cD1Qcm9ncmFtbWVDYXRlZ29yeSwgCiAgICAgICAgICAgICBjb2xvcj1Qcm9ncmFtbWVDYXRlZ29yeQogICAgICAgICAgICAgKQogICAgKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBnZW9tX3BvaW50KCkgCiAgCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoKIyBDSEFMTEVOR0U6IGxpbmUgcGxvdCBmb3IgaGlib3JfZml4aW5nXzFtCmBgYHtyfQpsaWJyYXJ5KGpzb25saXRlKSAjIGxvYWQgcGFja2FnZQpoa21hLmludGVyYmFuay51cmwgPSAiaHR0cHM6Ly9hcGkuaGttYS5nb3YuaGsvcHVibGljL21hcmtldC1kYXRhLWFuZC1zdGF0aXN0aWNzL2RhaWx5LW1vbmV0YXJ5LXN0YXRpc3RpY3MvZGFpbHktZmlndXJlcy1pbnRlcmJhbmstbGlxdWlkaXR5IgppbnRlcmJhbmsubGlxdWlkaXR5ID0gZnJvbUpTT04oaGttYS5pbnRlcmJhbmsudXJsKQojIHRoZSBhYm92ZSByZXRyaWV2YWwgd2lsbCB0YWtlIGEgd2hpbGUuICBUaGUgc2VydmVyIHJlc3BvbnNlIGlzIHNsb3cuCnN1bW1hcnkoaW50ZXJiYW5rLmxpcXVpZGl0eSkKc3RyKGludGVyYmFuay5saXF1aWRpdHkpCmludGVyYmFuay5saXF1aWRpdHkkcmVzdWx0CnN0cihpbnRlcmJhbmsubGlxdWlkaXR5JHJlc3VsdCkKaW50ZXJiYW5rLnJlY29yZHMgPSBpbnRlcmJhbmsubGlxdWlkaXR5JHJlc3VsdCRyZWNvcmRzICU+JSBhc190aWJibGUoKQppbnRlcmJhbmsucmVjb3JkcwoKaW50ZXJiYW5rLnJlY29yZHMgJT4lIApnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKAogICAgbWFwcGluZz1hZXMoeD1lbmRfb2ZfZGF0ZSwgeT1oaWJvcl9maXhpbmdfMW0sIGdyb3VwPTEpLAogICAgY29sb3I9Im9yYW5nZSIKICAgICAgICAgICAgICkKCmBgYAoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBHUk9VUElORyBBTkQgQUdHUkVHQVRJT04KCiMjIFVzaW5nIGdyb3VwX2J5KCkgYW5kIHN1bW1hcmlzZSgpIApgYGB7cn0KZ3JhZHVhdGVzICU+JSBncm91cF9ieShBY2FkZW1pY1llYXIsIExldmVsT2ZTdHVkeSkgJT4lIAogIHN1bW1hcmlzZShUb3RhbEhlYWRjb3VudCA9IHN1bShIZWFkY291bnQpKSAKCmdyYWR1YXRlcyAlPiUgZ3JvdXBfYnkoQWNhZGVtaWNZZWFyLCBMZXZlbE9mU3R1ZHkpICU+JSAKICBzdW1tYXJpc2UoVG90YWxIZWFkY291bnQgPSBzdW0oSGVhZGNvdW50KSkgJT4lIAogIGdncGxvdCgKICAgIGFlcyh4PUFjYWRlbWljWWVhciwgCiAgICAgICAgICAgICB5PVRvdGFsSGVhZGNvdW50LAogICAgICAgICAgICAgZ3JvdXA9TGV2ZWxPZlN0dWR5LCAKICAgICAgICAgICAgIGNvbG9yPUxldmVsT2ZTdHVkeQogICAgICAgICAgICAgKQogICAgKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBnZW9tX3BvaW50KCkgCiAgCmBgYAoKIyMgVXNlIG9mIGZpbHRlcigpClVzZSBmaWx0ZXIoKSB0byBrZWVwIG9ubHkgIlRhdWdodCBQb3N0Z3JhZHVhdGUiIFJlY29yZHMKClRoaXMgcGxvdCBpcyBub3QgdmVyeSB1c2VmdWwgd2l0aG91dCBwcmV2aW91c2x5IGFwcGx5aW5nIGZpbHRlcigpIGFuZCBncm91cF9ieSgpIGFuZCBzdW1tYXJpc2UoKQoKYGBge3J9CmdyYWR1YXRlcyAlPiUgCiAgZmlsdGVyKExldmVsT2ZTdHVkeT09IlRhdWdodCBQb3N0Z3JhZHVhdGUiKSAlPiUgCiAgZ2dwbG90KCkgKwogICAgZ2VvbV9saW5lKG1hcHBpbmc9YWVzKHg9QWNhZGVtaWNZZWFyLHk9SGVhZGNvdW50LCBncm91cD1Qcm9ncmFtbWVDYXRlZ29yeSwgY29sb3I9UHJvZ3JhbW1lQ2F0ZWdvcnkpKQpgYGAKCgojIyBmaWx0ZXIoKSArIGdyb3VwX2J5KCkgKyBzdW1tYXJpc2UoKQpVc2UgZmlsdGVyKCkgdG8gZXh0cmFjdCByZXF1aXJlZCByb3dzClVzZSBncm91cF9ieSgpIGFuZCBzdW1tYXJpc2UoKSB0byBncm91cCBhbmQgYWdncmVhdGUgdG90YWwgaGVhZGNvdXQgZm9yIGJvdGggbWFsZSBhbmQgZmVtYWxlCmBgYHtyIGdncGxvdCBsaW5lfQoKZ3JhZHVhdGVzICU+JSAKICBmaWx0ZXIoTGV2ZWxPZlN0dWR5PT0iVGF1Z2h0IFBvc3RncmFkdWF0ZSIpICU+JSAKICBncm91cF9ieShBY2FkZW1pY1llYXIsIFByb2dyYW1tZUNhdGVnb3J5KSAlPiUgCiAgc3VtbWFyaXNlKFRvdGFsSGVhZGNvdW50ID0gc3VtKEhlYWRjb3VudCkpICU+JSAKICBnZ3Bsb3QoKSArCiAgICBnZW9tX2xpbmUobWFwcGluZz1hZXMoeD1BY2FkZW1pY1llYXIseT1Ub3RhbEhlYWRjb3VudCwgZ3JvdXA9UHJvZ3JhbW1lQ2F0ZWdvcnksIGNvbG9yPVByb2dyYW1tZUNhdGVnb3J5KSkKCgojIGdyYWR1YXRlcyAlPiUgCiMgICBmaWx0ZXIoTGV2ZWxPZlN0dWR5PT0iVW5kZXJncmFkdWF0ZSIpICU+JSAKIyAgIGdyb3VwX2J5KEFjYWRlbWljWWVhciwgUHJvZ3JhbW1lQ2F0ZWdvcnkpICU+JSAKIyAgIHN1bW1hcmlzZShUb3RhbEhlYWRjb3VudCA9IHN1bShIZWFkY291bnQpKSAlPiUgCiMgICBnZ3Bsb3QoKSArCiMgICAgIGdlb21fbGluZShtYXBwaW5nPWFlcyh4PUFjYWRlbWljWWVhcix5PVRvdGFsSGVhZGNvdW50LCBncm91cD1Qcm9ncmFtbWVDYXRlZ29yeSwgY29sb3I9UHJvZ3JhbW1lQ2F0ZWdvcnkpKQogICAgCmBgYAoKCgojIyBnZW9tX2NvbCgpIGZ1bmN0aW9uCgpgYGB7cn0KTGV2ZWxPZlN0dWR5ID0gZ3JhZHVhdGVzICU+JSAuJExldmVsT2ZTdHVkeSAlPiUgdW5pcXVlKCkKUHJvZ3JhbW1lQ2F0ZWdvcnkgPSBncmFkdWF0ZXMgJT4lIC4kUHJvZ3JhbW1lQ2F0ZWdvcnkgJT4lIHVuaXF1ZSgpIApwcmludChMZXZlbE9mU3R1ZHkpCnByaW50KFByb2dyYW1tZUNhdGVnb3J5KQogIApncmFkdWF0ZXMgJT4lIApmaWx0ZXIoUHJvZ3JhbW1lQ2F0ZWdvcnk9PSJCdXNpbmVzcyBhbmQgTWFuYWdlbWVudCIpICU+JSAKZ2dwbG90KCkgKwogIGdlb21fY29sKG1hcHBpbmc9YWVzKHg9QWNhZGVtaWNZZWFyLCB5PUhlYWRjb3VudCwgZmlsbD1MZXZlbE9mU3R1ZHkpKQoKZ3JhZHVhdGVzICU+JSAKZmlsdGVyKFByb2dyYW1tZUNhdGVnb3J5PT0iRW5naW5lZXJpbmcgYW5kIFRlY2hub2xvZ3kiKSAlPiUgCmdncGxvdCgpICsKICBnZW9tX2NvbChtYXBwaW5nPWFlcyh4PUFjYWRlbWljWWVhciwgeT1IZWFkY291bnQsIGZpbGw9TGV2ZWxPZlN0dWR5KSkKCmBgYAoKIyMgTW9yZSBBZ2dyZWdhdGlvbiBGdW5jdGlvbnMKQ2VudGVyOiBtZWFuKCksIG1lZGlhbigpClNwcmVhZDogc2QoKSwgSVFSKCksIG1hZCgpClJhbmdlOiBtaW4oKSwgbWF4KCksIHF1YW50aWxlKCkKUG9zaXRpb246IGZpcnN0KCksIGxhc3QoKSwgbnRoKCksCkNvdW50OiBuKCksIG5fZGlzdGluY3QoKQpMb2dpY2FsOiBhbnkoKSwgYWxsKCkKCk1vcmUgaW5mb3JtYXRpb24gYXQKW3N1bW1hcmlzZSgpIGZ1bmN0aW9uXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3N1bW1hcmlzZS5odG1sKQoKCiMjIGdlb21fYmFyKCkgZnVuY3Rpb24KYmFyIGNoYXJ0IGdpdmUgdGhlIGNvdW50aW5nIGZyZXF1ZW5jeSAobnVtYmVyIG9mIHJlY29yZCBpbiB0aGUgZGF0YSBzZXQpCmBgYHtyfQoKZ3JhZHVhdGVzICU+JSAKZ2dwbG90KCkgKwogIGdlb21fYmFyKG1hcHBpbmc9YWVzKHg9QWNhZGVtaWNZZWFyKSkgIyB5b3Ugb25seSBuZWVkIHRvIHByb3ZpZGUgdGhlIHggY29sdW1uCmBgYAoKIyMgYm94IHBsb3QKVGhlIGJveHBsb3QgY29tcGFjdGx5IGRpc3BsYXlzIHRoZSBkaXN0cmlidXRpb24gb2YgYSBjb250aW51b3VzIHZhcmlhYmxlLlxuCkl0IHZpc3VhbGlzZXMgZml2ZSBzdW1tYXJ5IHN0YXRpc3RpY3MgKHRoZSBtZWRpYW4sIHR3byBoaW5nZXMgYW5kIHR3byB3aGlza2VycyksIGFuZCBhbGwgIm91dGx5aW5nIiBwb2ludHMgaW5kaXZpZHVhbGx5LgoKYGBge3J9CmdyYWR1YXRlcyAlPiUgCmdncGxvdCgpICsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9U2V4LCB5PUhlYWRjb3VudCkpCgpncmFkdWF0ZXMgJT4lIApnZ3Bsb3QoKSArCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmc9YWVzKHg9TGV2ZWxPZlN0dWR5LCB5PUhlYWRjb3VudCkpCmBgYAoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBNQUtFIElUIFBSRVRUWQpVc2Ugb2YgdGl0bGUsIGxhYmVsLCBiYWNrZ3JvdW5kIGNvbG9yIGFuZCB0aGVtZXMKCmBgYHtyfQpsZXZlbC5iYXIucGxvdCA9IGdyYWR1YXRlcyAlPiUgCmZpbHRlcihQcm9ncmFtbWVDYXRlZ29yeT09IkVuZ2luZWVyaW5nIGFuZCBUZWNobm9sb2d5IikgJT4lIApnZ3Bsb3QoKSArCiAgZ2VvbV9jb2wobWFwcGluZz1hZXMoeD1BY2FkZW1pY1llYXIsIHk9SGVhZGNvdW50LCBmaWxsPUxldmVsT2ZTdHVkeSkpCmBgYAoKIyMgUGxvdCBCYWNrZ3JvdW5kCmBgYHtyfQpsZXZlbC5iYXIucGxvdCAjIGRlZmF1bHQgc3R5bGUKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD0ib3JhbmdlIikpICMgc3R5bGluZyB0aGUgcGxvdCBiYWNrZ3JvdW5kCmBgYAoKIyMgUGFuZWwgQmFja2dyb3VuZApgYGB7cn0KbGV2ZWwuYmFyLnBsb3QgIyBkZWZhdWx0IHN0eWxlCgpsZXZlbC5iYXIucGxvdCArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPSJvcmFuZ2UiKSkgIyBzdHlsaW5nIHRoZSBwYW5lbCBiYWNrZ3JvdW5kCmBgYAoKIyMgUmVtb3ZlIFBsb3QgYW5kIFBhbmVsIEJhY2tncm91bmQKYGBge3J9CmxldmVsLmJhci5wbG90ICMgZGVmYXVsdCBzdHlsZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwYW5lbCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwbG90IGJhY2tncm91bmQgdG8gbm9uZQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvcj0iZ3JleSIpKSAjIHN0eWxpbmcgdGhlIGdyaWQgbGluZSBmb3IgeS1heGlzCmBgYAoKIyMgTGFiZWwKYGBge3J9CmxldmVsLmJhci5wbG90ICMgZGVmYXVsdCBzdHlsZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwYW5lbCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwbG90IGJhY2tncm91bmQgdG8gbm9uZQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvcj0iZ3JleSIpKSArICMgc3R5bGluZyB0aGUgZ3JpZCBsaW5lIGZvciB5LWF4aXMKICB5bGFiKCJOdW1iZXIgb2YgU3R1ZGVudCIpICsgIyBMYWJlbCBmb3IgWSBheGlzCiAgeGxhYigiWWVhciIpICMgTGFiZWwgZm9yIFggYXhpcwpgYGAKCiMjIENoYW5nZSBGaWxsIENvbG9ycwpgYGB7cn0KbGV2ZWwuYmFyLnBsb3QgIyBkZWZhdWx0IHN0eWxlCgpsZXZlbC5iYXIucGxvdCArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAjIHN0eWxpbmcgdGhlIHBhbmVsIGJhY2tncm91bmQgdG8gbm9uZQogIHRoZW1lKHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAjIHN0eWxpbmcgdGhlIHBsb3QgYmFja2dyb3VuZCB0byBub25lCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9saW5lKGNvbG9yPSJncmV5IikpICsgIyBzdHlsaW5nIHRoZSBncmlkIGxpbmUgZm9yIHktYXhpcwogIHlsYWIoIk51bWJlciBvZiBTdHVkZW50IikgKyAjIExhYmVsIGZvciBZIGF4aXMKICB4bGFiKCJZZWFyIikgKyAjIExhYmVsIGZvciBYIGF4aXMKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygicHVycGxlIiwgIm9yYW5nZSIsICJibHVlIiwgInRvbWF0byIpKSAjIHVzZSBjKCkgZnVuY3Rpb24gdG8gc3BlY2lmeSBjb2xvciBsaXN0CmBgYAoKIyMgU3R5bGluZyBMZWdlbmRzCmBgYHtyfQpsZXZlbC5iYXIucGxvdCAjIGRlZmF1bHQgc3R5bGUKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArICMgc3R5bGluZyB0aGUgcGFuZWwgYmFja2dyb3VuZCB0byBub25lCiAgdGhlbWUocGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArICMgc3R5bGluZyB0aGUgcGxvdCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3I9ImdyZXkiKSkgKyAjIHN0eWxpbmcgdGhlIGdyaWQgbGluZSBmb3IgeS1heGlzCiAgeWxhYigiTnVtYmVyIG9mIFN0dWRlbnQiKSArICMgTGFiZWwgZm9yIFkgYXhpcwogIHhsYWIoIlllYXIiKSArICMgTGFiZWwgZm9yIFggYXhpcwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0idG9wIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJwdXJwbGUiLCAib3JhbmdlIiwgImJsdWUiLCAidG9tYXRvIiksCiAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQodGl0bGU9IkxldmVsIG9mIFN0dWR5IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwucG9zaXRpb24gPSAiYm90dG9tIikKICAgICAgICAgICAgICAgICAgICApICMgbW92ZSBsZWdlbmQgcG9zaXRpb24gdG8gdG9wIGFuZCBsYWJlbCBwb3NpdGlvbiB0byBib3R0b20gCiAgCmBgYAoKIyMgVGl0bGUKYGBge3J9CmxldmVsLmJhci5wbG90ICMgZGVmYXVsdCBzdHlsZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwYW5lbCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwbG90IGJhY2tncm91bmQgdG8gbm9uZQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvcj0iZ3JleSIpKSArICMgc3R5bGluZyB0aGUgZ3JpZCBsaW5lIGZvciB5LWF4aXMKICB5bGFiKCJOdW1iZXIgb2YgU3R1ZGVudCIpICsgIyBMYWJlbCBmb3IgWSBheGlzCiAgeGxhYigiWWVhciIpICsgIyBMYWJlbCBmb3IgWCBheGlzCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoInB1cnBsZSIsICJvcmFuZ2UiLCAiYmx1ZSIsICJ0b21hdG8iKSwKICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iTGV2ZWwgb2YgU3R1ZHkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5wb3NpdGlvbiA9ICJib3R0b20iKQogICAgICAgICAgICAgICAgICAgICkgKyAjIG1vdmUgbGVnZW5kIHBvc2l0aW9uIHRvIHRvcCBhbmQgbGFiZWwgcG9zaXRpb24gdG8gYm90dG9tIAogIGdndGl0bGUoIkhvbmcgS29uZyBIaWdoZXIgRWR1Y2F0aW9uIFN0dWRlbnQgSGVhZGNvdW50Iiwgc3VidGl0bGU9IjIwMDkgLSAyMDE5IikKYGBgCgojIyBBZGRpbmcgQW5ub3RhdGlvbnMgVGV4dApBZGQgZXh0cmEgdGV4dHMvc2hhcGUgdG8gZW5oYW5jZSB5b3VyIHZpc3VhbGl6YXRpb24KYGBge3J9CmxldmVsLmJhci5wbG90ICMgZGVmYXVsdCBzdHlsZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwYW5lbCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwbG90IGJhY2tncm91bmQgdG8gbm9uZQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvcj0iZ3JleSIpKSArICMgc3R5bGluZyB0aGUgZ3JpZCBsaW5lIGZvciB5LWF4aXMKICB5bGFiKCJOdW1iZXIgb2YgU3R1ZGVudCIpICsgIyBMYWJlbCBmb3IgWSBheGlzCiAgeGxhYigiWWVhciIpICsgIyBMYWJlbCBmb3IgWCBheGlzCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoInB1cnBsZSIsICJvcmFuZ2UiLCAiYmx1ZSIsICJ0b21hdG8iKSwKICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iTGV2ZWwgb2YgU3R1ZHkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5wb3NpdGlvbiA9ICJib3R0b20iKQogICAgICAgICAgICAgICAgICAgICkgKyAjIG1vdmUgbGVnZW5kIHBvc2l0aW9uIHRvIHRvcCBhbmQgbGFiZWwgcG9zaXRpb24gdG8gYm90dG9tIAogIGdndGl0bGUoIkhvbmcgS29uZyBIaWdoZXIgRWR1Y2F0aW9uIFN0dWRlbnQgSGVhZGNvdW50Iiwgc3VidGl0bGU9IjIwMDkgLSAyMDE5IikgKyAKICBhbm5vdGF0ZSgidGV4dCIsIGxhYmVsPSJSZWNvcmRcbkhpZ2giLCB4PSIyMDE3LzE4IiwgeT01MzAwKSAjIHlvdSBjYW4gY2hhbmdlIHRleHQgcG9zaXRpb24gdmFsdWUgb2YgeCBhbmQgeSB0byBzZXQgdGhlIHRleHQgcG9zaXRpb24KYGBgCgojIyBBZGRpbmcgUmVmZXJlbmNlIExpbmUKYGBge3J9CmxldmVsLmJhci5wbG90ICMgZGVmYXVsdCBzdHlsZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwYW5lbCBiYWNrZ3JvdW5kIHRvIG5vbmUKICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBzdHlsaW5nIHRoZSBwbG90IGJhY2tncm91bmQgdG8gbm9uZQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvcj0iZ3JleSIpKSArICMgc3R5bGluZyB0aGUgZ3JpZCBsaW5lIGZvciB5LWF4aXMKICB5bGFiKCJOdW1iZXIgb2YgU3R1ZGVudCIpICsgIyBMYWJlbCBmb3IgWSBheGlzCiAgeGxhYigiWWVhciIpICsgIyBMYWJlbCBmb3IgWCBheGlzCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoInB1cnBsZSIsICJvcmFuZ2UiLCAiYmx1ZSIsICJ0b21hdG8iKSwKICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZT0iTGV2ZWwgb2YgU3R1ZHkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5wb3NpdGlvbiA9ICJib3R0b20iKQogICAgICAgICAgICAgICAgICAgICkgKyAjIG1vdmUgbGVnZW5kIHBvc2l0aW9uIHRvIHRvcCBhbmQgbGFiZWwgcG9zaXRpb24gdG8gYm90dG9tIAogIGdndGl0bGUoIkhvbmcgS29uZyBIaWdoZXIgRWR1Y2F0aW9uIFN0dWRlbnQgSGVhZGNvdW50Iiwgc3VidGl0bGU9IjIwMDkgLSAyMDE5IikgKyAKICBhbm5vdGF0ZSgidGV4dCIsIGxhYmVsPSJSZWNvcmRcbkhpZ2giLCB4PSIyMDE3LzE4IiwgeT01MzAwKSArICMgeW91IGNhbiBjaGFuZ2UgdGV4dCBwb3NpdGlvbiB2YWx1ZSBvZiB4IGFuZCB5IHRvIHNldCB0aGUgdGV4dCBwb3NpdGlvbgogIGdlb21faGxpbmUoeWludGVyY2VwdD0zMjAwKSArICMgYWRkcyBob3Jpem9udGFsIGxpbmUKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAiMjAxNy8xOCIpICMgYWRkcyB2ZXJ0aWNhbCBsaW5lCiAgCmBgYAoKCiMjIFVzaW5nIFRoZW1lcwpgYGB7cn0KbGV2ZWwuYmFyLnBsb3QgIyBkZWZhdWx0IHN0eWxlCgpsZXZlbC5iYXIucGxvdCArCiAgdGhlbWVfYncoKSAjIGJsYWNrIGFuZCB3aGl0ZSB0aGVtZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lX21pbmltYWwoKSAjIGJsYWNrIGFuZCB3aGl0ZSB0aGVtZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lX2RhcmsoKSAjIGJsYWNrIGFuZCB3aGl0ZSB0aGVtZQpgYGAKCiMjICBNb3JlIDNyZC1wYXJ0eSBUaGVtZXMKSW5zdGFsbCAqKmdndGhlbWVzKiogcGFja2FnZSB0byB1bmxvY2sgd2lkZXIgc2VsZWN0aW9ucyBvZiB0aGVtZXMuCgpgYGB7cn0KaWYgKCFyZXF1aXJlKCJwYWNtYW4iKSkgaW5zdGFsbC5wYWNrYWdlcygicGFjbWFuIikgIyBjaGVjayBpZiBwYWNtYW4gYWxyZWFkeSBpbnN0YWxsZWQuIElmIG5vdCwgaW5zdGFsbCBpdC4KcGFjbWFuOjpwX2xvYWQoZ2d0aGVtZXMpCgpsZXZlbC5iYXIucGxvdCAjIGRlZmF1bHQgc3R5bGUKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZV9leGNlbCgpICMgRXhjZWwgVGhlbWUKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZV93c2ooKSAjIFdhbGwgU3RyZWV0IEpvdXJuYWwgVGhlbWUKCmxldmVsLmJhci5wbG90ICsKICB0aGVtZV9lY29ub21pc3QoKSAjIEVjb25vbWlzdCBUaGVtZQoKbGV2ZWwuYmFyLnBsb3QgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICMgV2FsbCBTdHJlZXQgSm91cm5hbCBUaGVtZQoKYGBgCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIE1PUkUgUkVTT1VSQ0VTIE9OIGdncGxvdDIKIyMgb2ZmaWNpYWwgd2Vic2l0ZQpgYGB7cn0KYnJvd3NlVVJMKCJodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy8iKQpgYGAKCiMjIGV4dGVudHNpb25zCmBgYHtyfQpicm93c2VVUkwoImh0dHBzOi8vZXh0cy5nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvIikKYGBgCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoKIyBNT0RFTElORwoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoKIyBUTyBDT05USU5VRQoKIyMgdW5uZXN0KCkKCiMjIENhdGVnb3JpY2FsIFZhcmlhYmxlCgojIyBSZWNvZGluZyBEYXRhCgojIyBTY2FsaW5nCgojIyBUcmFuc2Zvcm1pbmcgT3V0bGllcnMK